// js 沙盒
// 沙盒要处于活动状态才会执行
// 主要绑定window下的属性已经全局性的事件
// 移除的时候，会解绑并且会移除所有子应用的绑定
export class Sandbox {
  active = false // 沙盒是否在运行
  microWindow = {} // 代理的对象
  injectedKeys = new Set() // 新添加的元素，卸载时清空
  eventObject = {} //新添加的绑定时间
  constructor() {
    this.proxyWindow = new Proxy(this.microWindow, {
      get: (target, key) => {
        if (Reflect.has(target, key)) {
          return Reflect.get(target, key)
        }
        const returnValue = Reflect.get(window, key)
        // 如果兜底的值为函数，则需要绑定window对象，如：console、alert等
        if (typeof returnValue === 'function') {
          const valueStr = returnValue.toString()
          // 绑定在window下的函数会继续绑定在window下,
          // 比如addEventListener，如果不修改绑定会报错Illegal invocation，非法调用
          if (!/^function\s+[A-Z]/.test(valueStr) && !/^class\s+/.test(valueStr)) {
            return returnValue.bind(window)
          }
        }
        return returnValue
      },
      set: (target, key, value) => {
        if (this.active) {
          Reflect.set(target, key, value)
        }
        this.injectedKeys.add(key)
        return true
      },
      deleteProperty: (target, key) => {
        if (target.hasOwnProperty(key)) {
          return Reflect.deleteProperty(target, key)
        }
        return true
      }
    })
  }
  start() {
    if (!this.active) {
      this.active = true
      this.effect()
    }
  }
  stop() {
    if (this.active) {
      this.active = false
      for (let key of this.injectedKeys) {
        Reflect.deleteProperty(this.microWindow, key)
      }
      this.injectedKeys.clear()
      this.relaeaseEffect()
    }
  }
  // 使用with改变函数的作用域，返回字符串，再用eval或者new Function执行
  bindScope(code) {
    window.proxyWindow = this.proxyWindow
    return `;(function(window, self){with(window){;${code}\n}}).call(window.proxyWindow, window.proxyWindow, window.proxyWindow);`
  }
  // 修改全局绑定事件
  effect() {
    // 备份原来的
    this.pre = {
      add: window.addEventListener,
      remove: window.removeEventListener
    }
    this.proxyWindow.addEventListener = (name, cb, useCapture) => {
      let list = this.eventObject[name]
      if (list instanceof Array) {
        if (list.indexOf(cb) !== 1) {
          list.push(cb)
          // 如果不修改绑定会报错Illegal invocation，非法调用
          this.pre.add.call(window, name, cb, useCapture)
        }
      } else {
        this.eventObject[name] = [cb]
        // 如果不修改绑定会报错Illegal invocation，非法调用
        this.pre.add.call(window, name, cb, useCapture)
      }
    }
    this.proxyWindow.removeEventListener = (name, cb) => {
      let list = this.eventObject[name]
      if (list instanceof Array) {
        let index = list.indexOf(cb)
        if (index !== 1) {
          list.splice(index, 1)
          // 如果不修改绑定会报错Illegal invocation，非法调用
          this.pre.remove.call(window, name, cb)
        }
      }
    }
  }
  // 还原全局绑定事件,并且移除当前所有的事件监听
  relaeaseEffect() {
    this.proxyWindow.addEventListener = this.pre.add
    this.proxyWindow.removeEventListener = this.pre.remove
    Object.entries(this.eventObject).forEach(item => {
      item[1].forEach(event => {
        window.removeEventListener(item[0], event)
      })
    })
  }
}
